package com.ansion.commonchat.startup.workmanager.bwork.impl.mode;

import com.ansion.commonchat.startup.workmanager.bwork.BData;
import com.ansion.commonchat.startup.workmanager.bwork.BWorkInfo;
import com.ansion.log.LogTag;
import com.ansion.log.VLog;
import com.ansion.utils.GsonUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wanghongjie on 2023/5/5
 * Describe :  等同于 Database WorkSpec 表的 API实现 ，是数据缓存中最主要的类
 */
public class BWorkSpecImpl implements BWorkSpecDao {
    private WorkDataCache workDataCache;
    private final List<BWorkSpec> workSpecs = new ArrayList<>();
    private final Map<String, BWorkSpec> workSpecsForId = new HashMap();
    private final Object mLock = new Object();

    public BWorkSpecImpl(WorkDataCache workDataCache) {
        this.workDataCache = workDataCache;
    }

    @Override
    public void insertWorkSpec(BWorkSpec workSpec) {
        synchronized (mLock) {
            workSpecs.add(workSpec);
            workSpecsForId.put(workSpec.id, workSpec);
            if (VLog.OPEN_LOG)
                VLog.d(LogTag.TAG_B_WORK_MANAGER, " BWorkSpecDao insertWorkSpec workSpec >>   %s ", GsonUtil.GsonString(workSpec));
        }
    }

    @Override
    public void delete(String id) {
        synchronized (mLock) {
            BWorkSpec workSpec = workSpecsForId.remove(id);
            workSpecs.remove(workSpec);
        }
    }

    /**
     * @Query("SELECT * FROM workspec WHERE id=:id")
     */
    @Override
    public BWorkSpec getWorkSpec(String id) {
        return workSpecsForId.get(id);
    }

    /**
     * @Query("UPDATE workspec SET state=:state WHERE id IN (:ids)")
     */
    @Override
    public void setState(BWorkInfo.State state, String id) {
        synchronized (mLock) {
            BWorkSpec workSpec = getWorkSpec(id);
            if (workSpec != null) {
                int index = workSpecs.indexOf(workSpec);
                workSpec.state = state;
                if (VLog.OPEN_LOG)
                    VLog.d(LogTag.TAG_B_WORK_MANAGER, " BWorkSpecDao setState index >>   %s , state >> %s , workSpecId >> %s ", index, state, id);
                // 通知监听的work状态已经变更
                workDataCache.workCallbackDao().notifyWorkStateChange(id);
            }
        }
    }

    /**
     * @Query("UPDATE workspec SET output=:output WHERE id=:id")
     */
    @Override
    public void setOutput(String id, BData output) {
        synchronized (mLock) {
            BWorkSpec workSpec = getWorkSpec(id);
            workSpec.output = output;
        }
    }

    /**
     * @Query("SELECT state FROM workspec WHERE id=:id")
     */
    @Override
    public BWorkInfo.State getState(String id) {
        BWorkSpec workSpec = getWorkSpec(id);
        if (workSpec != null) {
            return workSpec.state;
        }
        return null;
    }

    /**
     * 需要跨表查，tags 在 WorkTag 里，其余属性在 WorkSpec中
     *
     * @Query("SELECT id, state, output, run_attempt_count FROM workspec WHERE id=:id")
     */
    @Override
    public BWorkSpec.BWorkInfoPojo getWorkStatusPojoForId(String id) {
        BWorkSpec workSpec = getWorkSpec(id);
        if (workSpec != null) {
            BWorkSpec.BWorkInfoPojo pojo = new BWorkSpec.BWorkInfoPojo();
            pojo.id = workSpec.id;
            pojo.state = workSpec.state;
            pojo.output = workSpec.output;
            pojo.tags = workDataCache.workTagDao().getTagsForWorkSpecId(id);
            return pojo;
        }
        return null;
    }

    /**
     * Need to query across tables, tags are in WorkTag, other attributes are in WorkSpec
     *
     * @Query("SELECT id, state, output, run_attempt_count FROM workspec WHERE id IN "
     * + "(SELECT work_spec_id FROM worktag WHERE tag=:tag)")
     */
    @Override
    public List<BWorkInfo> getWorkInfosForTag(String tag) {
        List<String> ids = workDataCache.workTagDao().getWorkSpecIdsWithTag(tag);
        List<BWorkInfo> workInfoPojoList = new ArrayList<>();
        if (ids != null && !ids.isEmpty()) {
            for (String id : ids) {
                BWorkSpec.BWorkInfoPojo workInfoPojo = getWorkStatusPojoForId(id);
                if (workInfoPojo != null) workInfoPojoList.add(workInfoPojo.toWorkInfo());
            }
        }
        return workInfoPojoList;
    }

    /**
     * 应该是不需要根据period_start_time排序，简单场景，后进后出。
     *
     * @Query("SELECT * FROM workspec WHERE "
     * + "state=" + WorkTypeConverters.StateIds.ENQUEUED
     * + " ORDER BY period_start_time"
     * + " LIMIT :maxLimit"
     * )
     */
    @Override
    public List<BWorkSpec> getAllEligibleWorkSpecsForScheduling() {
        List<BWorkSpec> enqueuedWorkSpecs = new ArrayList<>();
        for (BWorkSpec workSpec : workSpecs) {
            if (workSpec.state == BWorkInfo.State.ENQUEUED) {
                enqueuedWorkSpecs.add(workSpec);
            }
        }
        return enqueuedWorkSpecs;
    }

    /**
     * 用于跨表查询，BDependencyDao#hasCompletedAllPrerequisites
     * SELECT id FROM workspec WHERE state!= WorkTypeConverters.StateIds.SUCCEEDED
     */
    @Override
    public List<String> getAllUnSucceededWorkSpecIds() {
        List<String> ids = new ArrayList<>();
        for (Map.Entry<String, BWorkSpec> entry : workSpecsForId.entrySet()) {
            BWorkSpec workSpec = entry.getValue();
            if (workSpec.state != BWorkInfo.State.SUCCEEDED) {
                ids.add(entry.getKey());
            }
        }
        return ids;
    }

    @Override
    public void logout() {
        synchronized (mLock) {
            workSpecs.clear();
            workSpecsForId.clear();
        }
    }
}
